package org.cainiao.basic.concurrent.lock;

import static org.cainiao.util.Util.println;
import static org.cainiao.util.Util.silentSleep;

/**
 * f3执行时，已经打印了"f2() >>> 1"
 * 说明线程t1已经重入了monitor
 * monitor上锁的count为2
 * 在调用了monitor.wait();之后
 * 调用f3时并没有阻塞，说明wait()方法并不是count减1
 * 而是直接释放了锁，count清0（否则f3进不去）
 * <p>
 * 通过t2线程的试验，可以看出
 * t1中monitor.wait();的位置被notify并抢到锁之后
 * 不是从0开始count++递增，在f2()出栈时再去抢父方法的锁
 * 而是在wait的地方抢到锁后，count直接为2的，f2()出栈回到f1()时，不需要抢锁
 */
public class WaitNotifyTest {

    private static final Object monitor = new Object();

    private static void f1() {
        synchronized (monitor) {
            println("f1() >>> 1");
            f2();
            println("f1() >>> 2");
            silentSleep(5000);
            println("f1() >>> 3");
        }
        println("f1() >>> 4");
    }

    private static void f2() {
        synchronized (monitor) {
            println("f2() >>> 1");
            try {
                monitor.wait();
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            println("f2() >>> 2");
        }
        Thread t2 = new Thread(WaitNotifyTest::f3);
        t2.start();
        println("t2 started");
        silentSleep(1000);
    }

    private static void f3() {
        synchronized (monitor) {
            println("f3() >>> 1");
            silentSleep(3000);
            println("f3() >>> 2");
        }
    }

    public static void main(String[] args) {
        Thread t1 = new Thread(WaitNotifyTest::f1);
        t1.start();
        silentSleep(1000);
        synchronized (monitor) {
            println("main() >>> 1");
            monitor.notify();
            silentSleep(1000);
            println("main() >>> 2");
        }
    }

}
